


# 原始NODE_COORD_SECTION转换CVRP格式
raw_data1 = """1 42 68
2 77 97
3 28 64
4 77 39
5 32 33
6 32 8
7 42 92
8 8 3
9 7 14
10 82 17
11 48 13
12 53 82
13 39 27
14 7 24
15 67 98
16 54 52
17 72 43
18 73 3
19 59 77
20 58 97
21 23 43
22 68 98
23 47 62
24 52 72
25 32 88
26 39 7
27 17 8
28 38 7
29 58 74
30 82 67
31 42 7
32 68 82
33 7 48"""

# 将数据转换成列表
data_list1 = raw_data1.split('\n')

# 将列表赋值给变量s
s1 = data_list1

# 输出变量s2
print("变量s的值:")
print(s1)




# 原始DEMAND_SECTION转换CVRP格式
raw_data2 = """1 0 
2 5 
3 23 
4 14 
5 13 
6 8 
7 18 
8 19 
9 10 
10 18 
11 20 
12 5 
13 9 
14 23 
15 9 
16 18 
17 10 
18 24 
19 13 
20 14 
21 8 
22 10 
23 19 
24 14 
25 13 
26 14 
27 2 
28 23 
29 15 
30 8 
31 20 
32 24 
33 3 """

# 将数据转换成列表
data_list2 = raw_data2.split('\n')

# 将列表赋值给变量s
s2 = data_list2

# 输出变量s2
print("变量s的值:")
print(s2)



def solution_routes(x):
    '''
    解析最佳解
    使用solution_routes(x)函数将最佳解（客户访问顺序）转换为一组路线，每条路线的总需求量不超过车辆的最大容量。
    :param x:
    :return:
    '''
    routes, route, route_sum = [], [], 0
    for i in x:
        if route_sum + session.get("demand")[i] >= session.get("max_capacity"):
            routes.append(route)
            route, route_sum = [], 0
        route.append(i)
        route_sum += session.get("demand")[i]
    if (len(route) > 0):
        routes.append(route)

    return routes


def routes_cost(routes):
    '''
    通过routes_cost(routes)函数计算得到的路线集合的总成本，即所有路线的距离之和。
    :param routes:
    :return:
    '''
    cost = 0
    for r in routes:
        rlist = [0] + r + [0]
        for i in range(len(rlist) - 1):
            cost += session.get("weights")[rlist[i]][rlist[i + 1]]
    return cost


def fitness_function(x):
    routes = solution_routes(x)
    cost = routes_cost(routes)
    return 1 / cost


def selection(population, fitness_values):
    next_population = []
    for _ in range(session.get("population_size")):
        parent = random.choices(population, weights=fitness_values)[0]
        next_population.append(parent)

    return next_population


def mutation(population):
    mut_population = [x.copy() for x in population]
    for i in range(len(mut_population)):
        k = \
            random.choices(np.arange(2, len(session.get("demand")) - 1),
                           weights=(1 / (np.arange(2, len(session.get("demand")) - 1))) ** session.get("max_capacity"))[
                0]
        # k代表变异长度
        # print(k)
        idxs = list(set([random.randint(0, len(session.get("demand")) - 2) for _ in range(k)]))
        mut_population[i][idxs] = np.random.permutation(mut_population[i][idxs])
    all_population = population + mut_population
    fitness_values = [fitness_function(x) for x in all_population]
    p, topk = fitness_values, session.get("population_size")
    index_list = sorted(range(len(p)), key=lambda i: p[i], reverse=True)
    next_population = []
    for i in range(topk):
        if i < len(index_list):
            next_population.append(all_population[index_list[i]])
        else:
            break

    return next_population


# 迭代开始
# def genetic_algorithm():
#     population = [np.random.permutation(session.get("custom_list")).copy() for _ in
#                   range(session.get('population_size'))]
#     best_solution = max(population, key=fitness_function)
#
#     for genetic_num in range(session.get('n_generations')):
#         fitness_values = [fitness_function(x) for x in population]
#         next_population = selection(population, fitness_values)
#         next_population = mutation(next_population)
#         population = next_population
#         current_best_solution = max(population, key=fitness_function)
#         if fitness_function(current_best_solution) > fitness_function(best_solution):
#             best_solution = current_best_solution
#         print(genetic_num, 1 / fitness_function(best_solution))
#
#     return best_solution, fitness_function(best_solution)

def genetic_algorithm(checkpoint=10):
    population = [np.random.permutation(session.get("custom_list")).copy() for _ in
                  range(session.get("population_size"))]
    best_solution = max(population, key=fitness_function)
    best_fitness = fitness_function(best_solution)
    checkpoint_costs = [(0, 1 / best_fitness)]  # 初始成本，迭代次数为0

    for genetic_num in range(1, session.get("n_generations") + 1):
        fitness_values = [fitness_function(x) for x in population]
        next_population = selection(population, fitness_values,)
        next_population = mutation(next_population)
        population = next_population

        current_best_solution = max(population, key=fitness_function)
        current_best_cost = 1 / fitness_function(current_best_solution)
        if fitness_function(current_best_solution) > best_fitness:
            best_solution = current_best_solution
            best_fitness = fitness_function(best_solution)

        if genetic_num % checkpoint == 0:
            checkpoint_costs.append((genetic_num, current_best_cost))
            print(f"Checkpoint at generation {genetic_num}: Best Cost = {current_best_cost}")

    return best_solution, best_fitness, checkpoint_costs


# 固定随机种子
def set_seed(seed):
    np.random.seed(seed)
    random.seed(seed)


# 绘制随机解的成本分布图
def plot_routes(best_routes, coord, best_cost, demand):
    fig, ax = plt.subplots(figsize=(6, 6))
    ax.scatter(coord[0][0], coord[1][0], s=100, c='red')  # 红色代表仓库
    ax.scatter(coord[0][1:], coord[1][1:], s=demand[1:], c='blue')  # 蓝色代表客户

    for i, route in enumerate(best_routes):
        color = np.random.rand(3, )
        plot_list = [0] + route + [0]
        ax.plot(coord[0][plot_list], coord[1][plot_list], linewidth=1, color=color)
        ax.scatter(coord[0][plot_list], coord[1][plot_list], s=demand[plot_list], c=color,
                   label=f'路线 {i + 1}: {sum(demand[route])}')

    ax.set_title(f'路线数量: {len(best_routes)}, 总成本: {int(best_cost)}')
    ax.legend(loc='best', fontsize='medium')
    plt.show()

    # 定义目录
    directory = os.path.join(current_app.root_path, 'static', 'optimization_image')
    if not os.path.exists(directory):
        os.makedirs(directory)
    timestamp = datetime.now().strftime('%Y%m%d%H%M%S')
    filename = os.path.join(directory, f'{timestamp}_optimization_solution.png')

    # 尝试保存图像文件
    try:
        fig.savefig(filename, bbox_inches='tight')
        plt.close(fig)
        return filename
    except Exception as e:
        plt.close(fig)
        print(f"Failed to save the image: {e}")
        return None


@oe.route("/optimize", methods=["GET", 'POST'])
def optimize():
    if request.method == "GET":
        has_seen_help = session.get('has_seen_help', False)

        print(session.get("population_size"))
        print(session.get("n_generations"))
        print(session.get("max_capacity"))
        print(session.get("mutation_rate"))
        return render_template("optimize.html", has_seen_help=has_seen_help)

    if request.method == 'POST':
        # 更新 session 中的参数
        if 'file' not in request.files:
            return redirect(request.url)
        file = request.files.get('file')
        if file and allowed_file(file.filename):

            # 确保文件名安全
            timestamp = datetime.now().strftime('%Y%m%d%H%M%S')
            filename = str(timestamp) + '_' + file.filename
            # 保存文件
            file_path = os.path.join(current_app.config['UPLOAD_FOLDER'], filename)
            file.save(file_path)
            userinfo_id = session['user_info']['id']
            with open(file_path, 'r', encoding="utf-8") as f:
                data = f.read()
            # 将上传文件的数据转为字典格式
            processed_json_string = preprocess_json_string(data)
            instance_data = json.loads(processed_json_string)
            # 获取根目录路径并拼接路径
            root_dir = current_app.root_path
            static_dir = os.path.join(root_dir, 'static', 'instance_file')
            os.makedirs(static_dir, exist_ok=True)
            # 拼接输出路径，保存本地并去读内容
            output_path = os.path.join(static_dir, f'{timestamp}_instance.vrp')
            vrp.write_instance(output_path, instance_data)
            instance = vrp.read_instance(output_path)

            demand = instance['demand']
            weights = instance['edge_weight']
            custom_list = np.arange(1, len(demand))
            coord = instance['node_coord'].T
            population_size = session.get("population_size", 1000)
            n_generations = session.get("n_generations", 100)
            max_capacity = session.get("max_capacity", 100)
            mutation_rate = session.get("mutation_rate", 0.5)
            session["demand"] = demand.tolist()
            session["weights"] = weights.tolist()
            session["custom_list"] = custom_list.tolist()
            session['coord'] = coord.tolist()
            set_seed(0)
            best_solution, best_fitness, checkpoint_costs = genetic_algorithm(checkpoint=10)
            print('best_solution', best_solution)
            best_routes = solution_routes(best_solution)
            print(best_routes)
            best_cost = routes_cost(best_routes)
            print('current:', best_cost)

            # TRUNCATE TABLE notifications;

            file_path = plot_routes(best_routes, coord, best_cost, demand)
            static_index = file_path.find('\\static\\') + len('\\static\\')
            if static_index > 0:
                # 获取从'static'开始的相对路径
                relative_path = file_path[static_index:].replace('\\', '/')  # 替换所有的反斜杠为正斜杠
            else:
                relative_path = None  # 如果没有找到'static', 返回None

            # db.insert_db_user_history_records(userinfo_id, file_path, best_cost, relative_path,
            #                                   session.get('population_size'),
            #                                   session.get('n_generations'), session.get('max_capacity'),
            #                                   session.get('mutation_rate'))
            return render_template("optimize.html", result=best_cost, image_path=relative_path)
